# 创建可交互式水平可视化窗口
# 使用使用QGraphic创建
 # 模拟数据
# np.random.seed(0)
import sys
import numpy as np
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QFrame, QMainWindow, QMenu, QToolTip
)
from PyQt5.QtChart import QChart, QChartView, QLineSeries, QValueAxis
from PyQt5.QtGui import QPen, QColor, QPainter, QDrag, QFont, QCursor
from PyQt5.QtCore import Qt, QPointF, QMimeData


# 模拟数据
element_to_simulate = {
    "Mg": (0, 15, QColor(255,0,0)),
    "Al": (5, 20, QColor(0,150,255)),
    "Si": (10, 25, QColor(0,200,0)),
}
num_points = 50
well_id = 'A1'
depths = np.linspace(1000, 2000, num_points)
data = []
for i in range(num_points):
    row = {'井号': well_id, '深度': depths[i]}
    for element, (min_val, max_val, _) in element_to_simulate.items():
        row[element] = np.random.uniform(min_val, max_val)
    data.append(row)

class CurveLabel(QLabel):
    def __init__(self, name, color, parent=None):
        super().__init__(name, parent)
        self.setFixedHeight(24)
        self.setStyleSheet(f"background-color: {color.name()}; color: white; border-radius: 4px; padding:2px 8px;")
        self.setCursor(Qt.CursorShape.OpenHandCursor)
        self.name = name
        self.color = color
        self.drag_start_pos = None
        self.setMouseTracking(True)#鼠标悬停事件

    
    def enterEvent(self, a0):
        self.setStyleSheet(f"background-color: {self.color.name()}; color: white; border-radius: 4px; padding:2px 8px; border:2px solid #FFA500;")
        super().enterEvent(a0)

    def leaveEvent(self, a0):
        self.setStyleSheet(f"background-color: {self.color.name()}; color: white; border-radius: 4px; padding:2px 8px;")
        super().leaveEvent(a0)

    def mousePressEvent(self, ev):
        if ev is not None and hasattr(ev, 'button') and ev.button() == Qt.MouseButton.LeftButton:
            self.drag_start_pos = ev.pos()
        super().mousePressEvent(ev)

    def mouseMoveEvent(self, ev):
        if ev is not None and self.drag_start_pos is not None and hasattr(ev, 'pos') and (ev.pos() - self.drag_start_pos).manhattanLength() > 10:
            drag = QDrag(self)
            mime = QMimeData()
            mime.setText(self.name)
            drag.setMimeData(mime)
            drag.setProperty('curve_label', self)
            self.hide()
            drag.exec_(Qt.DropAction.MoveAction)
            self.show()
            self.drag_start_pos = None
        if ev is not None:
            super().mouseMoveEvent(ev)

class InteractiveChartView(QChartView):
    def __init__(self, chart, parent=None):
        super().__init__(chart, parent)
        self.setRenderHint(QPainter.Antialiasing)
        self.setMouseTracking(True)
        self._last_pos = None
        self._is_panning = False
        self._is_zooming = False
        self._orig_x_range = (chart.axisX().min(), chart.axisX().max())
        self._orig_y_range = (chart.axisY().min(), chart.axisY().max())

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            if event.modifiers() & Qt.ControlModifier:
                self._is_zooming = True#解释:控制键按下时，开始缩放
                self._last_pos = event.pos()#解释:记录鼠标按下时的位置
            elif event.modifiers() & Qt.ShiftModifier:
                self._is_panning = True#解释:shift键按下时，开始平移
                self._last_pos = event.pos()#解释:记录鼠标按下时的位置  
            elif event.modifiers() & Qt.AltModifier:
                # Alt+左键重置
                self.chart().axisX().setRange(*self._orig_x_range)
                self.chart().axisY().setRange(*self._orig_y_range)
        elif event.button() == Qt.RightButton:
            if event.modifiers() & Qt.ShiftModifier:
            # Alt+左键重置
                self.chart().axisX().setRange(*self._orig_x_range)
                self.chart().axisY().setRange(*self._orig_y_range)
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        chart = self.chart()
        if self._is_zooming and self._last_pos:
            dx = event.x() - self._last_pos.x()
            dy = event.y() - self._last_pos.y()
            # 横向缩放
            x_min, x_max = chart.axisX().min(), chart.axisX().max()
            y_min, y_max = chart.axisY().min(), chart.axisY().max()
            factor = 1 - dx * 0.01  # 缩放因子
            if factor < 0.1: factor = 0.1
            cx = (x_min + x_max) / 2
            width = (x_max - x_min) * factor
            chart.axisX().setRange(cx - width/2, cx + width/2)
            # 纵向缩放
            factor_y = 1 - dy * 0.01
            if factor_y < 0.1: factor_y = 0.1
            cy = (y_min + y_max) / 2
            height = (y_max - y_min) * factor_y
            chart.axisY().setRange(cy - height/2, cy + height/2)
            self._last_pos = event.pos()
        elif self._is_panning and self._last_pos:
            dx = event.x() - self._last_pos.x()
            dy = event.y() - self._last_pos.y()
            # 横向平移
            x_min, x_max = chart.axisX().min(), chart.axisX().max()
            y_min, y_max = chart.axisY().min(), chart.axisY().max()
            x_range = x_max - x_min
            y_range = y_max - y_min
            chart.axisX().setRange(x_min - dx * x_range / self.width(), x_max - dx * x_range / self.width())
            chart.axisY().setRange(y_min + dy * y_range / self.height(), y_max + dy * y_range / self.height())
            self._last_pos = event.pos()
        else:
            # 鼠标移动时显示坐标
            pos = event.pos()
            chart_pos = self.chart().mapToValue(pos)
            QToolTip.showText(QCursor.pos(), f"深度: {chart_pos.x():.2f}\n值: {chart_pos.y():.2f}")
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self._is_zooming = False
        self._is_panning = False
        self._last_pos = None
        QToolTip.hideText()
        super().mouseReleaseEvent(event)

    def wheelEvent(self, event):
        # 鼠标滚轮缩放
        chart = self.chart()
        angle = event.angleDelta().y()#解释:获取鼠标滚轮的滚动方向和距离
        factor = 0.9 if angle > 0 else 1.1
        x_min, x_max = chart.axisX().min(), chart.axisX().max()
        y_min, y_max = chart.axisY().min(), chart.axisY().max()
        cx = (x_min + x_max) / 2
        cy = (y_min + y_max) / 2
        width = (x_max - x_min) * factor
        height = (y_max - y_min) * factor
        chart.axisX().setRange(cx - width/2, cx + width/2)
        chart.axisY().setRange(cy - height/2, cy + height/2)
        event.accept()  # 只处理缩放，不传递给QScrollArea

class TrackWidget(QFrame):
    def __init__(self, series_info_list, xdata, parent=None, bg_index=0):
        super().__init__(parent)
        self.setFrameShape(QFrame.StyledPanel)
        self.setAcceptDrops(True)
        self.series_info_list = series_info_list  # [(name, color, ydata)]
        self.xdata = xdata
        self.bg_index = bg_index
        self.init_ui()

    def init_ui(self):
        layout = QHBoxLayout(self)
        # 交替背景色
        if self.bg_index % 2 == 0:
            self.setStyleSheet("background-color: #F7F7F7;")
        else:
            self.setStyleSheet("background-color: #E9F5FF;")
        # y轴标签区
        self.label_area = QWidget(self)
        self.label_layout = QVBoxLayout()
        self.label_layout.setSpacing(2)
        self.label_area.setLayout(self.label_layout)
        self.curve_labels = []
        for name, color, ydata in self.series_info_list:
            label = CurveLabel(name, color, parent=self.label_area)
            self.label_layout.addWidget(label)
            self.curve_labels.append(label)
        layout.addWidget(self.label_area)
        # 折线图
        self.chart = QChart()
        self.chart.setBackgroundVisible(False)
        self.chart.legend().hide()#隐藏图例
        self.axisX = QValueAxis()
        self.axisX.setTitleText("")
        self.axisX.setLabelFormat("%.1f")
        self.axisX.setTickCount(6)#解释:设置x轴刻度数量
        self.axisX.setRange(min(self.xdata), max(self.xdata))
        self.axisY = QValueAxis()
        self.axisY.setTitleText("")
        self.axisY.setLabelFormat("%.1f")
        self.axisY.setTickCount(6)
        all_y = []#解释:获取所有y轴数据
        for _, _, ydata in self.series_info_list:#解释:获取所有y轴数据
            all_y.extend(ydata)#解释:将所有y轴数据添加到all_y列表中
        self.axisY.setRange(min(all_y), max(all_y))#解释:设置y轴范围
        self.chart.addAxis(self.axisX, Qt.AlignmentFlag.AlignBottom)#解释:添加x轴到图表
        self.chart.addAxis(self.axisY, Qt.AlignmentFlag.AlignLeft)
        self.series_objs = []
        for name, color, ydata in self.series_info_list:
            series = QLineSeries()
            series.setName(name)
            pen = QPen(color)
            pen.setWidth(2)
            series.setPen(pen)
            for x, y in zip(self.xdata, ydata):
                series.append(QPointF(x, y))
            self.chart.addSeries(series)
            series.attachAxis(self.axisX)
            series.attachAxis(self.axisY)
            self.series_objs.append(series)

            # 连接 hovered 信号
            series.hovered.connect(self.handlePointHovered)

        self.chart_view = InteractiveChartView(self.chart)
        self.chart_view.setFixedHeight(320)
        layout.addWidget(self.chart_view)
        self.setLayout(layout)
        self.setMouseTracking(True)

        font = QFont("微软雅黑", 14)
        self.axisX.setLabelsFont(font)
        self.axisX.setTitleFont(font)
        self.axisY.setLabelsFont(font)
        self.axisY.setTitleFont(font)

        self.axisX.setGridLineVisible(True)
        self.axisY.setGridLineVisible(True)

        # 可选：设置网格线颜色和样式
        grid_pen = QPen(QColor(200, 200, 200), 1, Qt.DashLine)
        self.axisX.setGridLinePen(grid_pen)
        self.axisY.setGridLinePen(grid_pen)

    # 添加曲线
    def add_curve(self, name, color, ydata):
        self.series_info_list.append((name, color, ydata))  # 添加曲线信息到列表
        label = CurveLabel(name, color, parent=self.label_area)  # 创建曲线标签
        self.label_layout.addWidget(label)  # 将标签添加到标签布局中
        self.curve_labels.append(label)  # 将标签对象加入曲线标签列表
        series = QLineSeries()  # 创建折线对象
        series.setName(name)
        pen = QPen(color)
        pen.setWidth(2)
        series.setPen(pen)
        for x, y in zip(self.xdata, ydata):
            series.append(QPointF(x, y))  # 添加每个点到折线
        self.chart.addSeries(series)  # 添加折线到图表
        series.attachAxis(self.axisX)  # 绑定x轴
        series.attachAxis(self.axisY)  # 绑定y轴
        self.series_objs.append(series)  # 保存折线对象
        all_y = []
        for _, _, ydata in self.series_info_list:
            all_y.extend(ydata)
        self.axisY.setRange(min(all_y), max(all_y))  # 更新y轴范围

    # 删除曲线
    def remove_curve(self, name):
        idx = None
        for i, (n, _, _) in enumerate(self.series_info_list):
            if n == name:
                idx = i
                break
        if idx is not None:
            self.series_info_list.pop(idx)  # 移除曲线信息
            label = self.curve_labels.pop(idx)  # 移除标签对象
            label.setParent(None)  # 从界面移除标签
            series = self.series_objs.pop(idx)  # 移除折线对象
            self.chart.removeSeries(series)  # 从图表移除折线
            all_y = []
            for _, _, ydata in self.series_info_list:
                all_y.extend(ydata)
            if all_y:
                self.axisY.setRange(min(all_y), max(all_y))  # 更新y轴范围

    # 获取曲线信息
    def get_curve_info(self, name):
        for n, color, ydata in self.series_info_list:
            if n == name:
                return (n, color, ydata)
        return None

    # 槽函数
    def handlePointHovered(self, point, state):
        if state:
            # 鼠标悬停，显示坐标
            QToolTip.showText(QCursor.pos(), f"深度: {point.x():.2f}\n值: {point.y():.2f}")
            # 或者 self.statusBar().showMessage(f"深度: {point.x():.2f} 值: {point.y():.2f}")
        else:
            QToolTip.hideText()
            # 或者 self.statusBar().clearMessage()

# 多轨道拖拽示例主控件
class MultiTrackWidget(QWidget):
    def __init__(self, tracks_data, xdata):#解释:tracks_data:轨道数据,xdata:x轴数据
        super().__init__()
        self.xdata = xdata  # 公共x轴数据
        self.track_widgets = []  # 所有轨道控件
        # 拖拽插入指示条
        self.drag_indicator = QFrame()
        self.drag_indicator.setFixedHeight(10)
        self.drag_indicator.setStyleSheet("background-color: yellow; border: 1px solid orange;")
        self.drag_indicator.hide()
        # 合并指示条
        self.merge_indicator = QFrame()
        self.merge_indicator.setStyleSheet("background-color: rgba(255,255,0,120); border: 2px solid orange;")
        self.merge_indicator.hide()
        self.merge_target = None  # 合并目标轨道
        self.insert_index = None  # 插入目标索引
        self.init_ui(tracks_data)  # 初始化界面

    def init_ui(self, tracks_data):
        self.vlayout = QVBoxLayout()
        self.vlayout.setSpacing(5)
        # 创建每个轨道控件
        for idx, (name, ydata, color) in enumerate(tracks_data):
            track = TrackWidget([(name, color, ydata)], self.xdata, bg_index=idx)
            self.vlayout.addWidget(track)
            self.track_widgets.append(track)
        # x轴标签
        self.xaxis_label = QLabel(" "*10 + "深度/时间轴" + " "*10)
        self.xaxis_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.xaxis_label.setStyleSheet("font-weight:bold;font-size:16px;")
        self.vlayout.addWidget(self.xaxis_label)
        self.setLayout(self.vlayout)
        self.setAcceptDrops(True)  # 允许拖拽

    # 拖拽生成新轨道
    def add_new_track(self, info, from_track):
        idx = self.vlayout.indexOf(from_track)#解释:获取from_track在vlayout中的索引
        if idx == -1:#解释:如果from_track在vlayout中不存在,则将其添加到vlayout的末尾
            idx = len(self.track_widgets)#解释:如果from_track在vlayout中不存在,则将其添加到vlayout的末尾
        new_track = TrackWidget([info], self.xdata, bg_index=(idx+1)%2)#解释:创建新轨道
        self.vlayout.insertWidget(idx+1, new_track)#解释:将新轨道插入到vlayout中
        self.track_widgets.insert(idx+1, new_track)#解释:将新轨道插入到track_widgets中

    # 拖拽进入事件
    #这是 PyQt5 中 QWidget 的一个事件处理方法，用于处理拖拽进入控件区域时的事件。
    def dragEnterEvent(self, a0):
        if a0 is not None:
            a0.accept()

    # 拖拽移动事件，判断合并/插入位置并显示指示条
    def dragMoveEvent(self, a0):
        if a0 is None:
            return
        pos = a0.pos()
        self.merge_target = None
        self.insert_index = None
        found = False
        for i, track in enumerate(self.track_widgets):
            top = track.y()#解释:获取轨道顶部位置       
            bottom = top + track.height()#解释:获取轨道底部位置
            merge_zone_top = top + track.height() * 0.2#解释:获取合并区域顶部位置
            merge_zone_bottom = bottom - track.height() * 0.2#解释:获取合并区域底部位置
            if merge_zone_top < pos.y() < merge_zone_bottom:
                self._show_merge_indicator(track)  # 显示合并指示条
                self.merge_target = track
                self.insert_index = None
                track.setStyleSheet(track.styleSheet() + "border:2px solid #FFA500;")
                found = True
                break
            elif top <= pos.y() < merge_zone_top:
                self._show_insert_indicator(i)  # 显示插入指示条
                self.merge_target = None
                self.insert_index = i
                found = True
                break
            elif merge_zone_bottom <= pos.y() < bottom:
                self._show_insert_indicator(i+1)
                self.merge_target = None
                self.insert_index = i+1
                found = True
                break
            else:
                track.setStyleSheet(track.styleSheet().replace("border:2px solid #FFA500;", ""))
        if not found:
            self._show_insert_indicator(len(self.track_widgets))
            self.merge_target = None
            self.insert_index = len(self.track_widgets)
        a0.accept()

    # 拖拽离开事件，隐藏所有指示条
    def dragLeaveEvent(self, a0):
        self.drag_indicator.hide()
        self.merge_indicator.hide()
        for track in self.track_widgets:
            track.setStyleSheet(track.styleSheet().replace("border:2px solid #FFA500;", ""))
        if a0 is not None and hasattr(a0, 'accept'):
            a0.accept()

    # 拖拽释放事件，执行合并/插入/排序等操作
    def dropEvent(self, a0):
        self.drag_indicator.hide()
        self.merge_indicator.hide()
        for track in self.track_widgets:
            track.setStyleSheet(track.styleSheet().replace("border:2px solid #FFA500;", ""))
        if a0 is None:
            return
        src = a0.source()
        if isinstance(src, CurveLabel):  # 拖拽的是曲线标签
            for track in self.track_widgets:
                if src in track.curve_labels:
                    name = src.name
                    info = track.get_curve_info(name)
                    if info is not None:
                        track.remove_curve(name)  # 从原轨道移除
                        if not track.series_info_list:
                            self.vlayout.removeWidget(track)
                            self.track_widgets.remove(track)
                        if self.merge_target is not None:
                            self.merge_target.add_curve(*info)  # 合并到目标轨道
                        elif self.insert_index is not None:
                            new_track = TrackWidget([info], self.xdata, bg_index=self.insert_index%2)
                            self.vlayout.insertWidget(self.insert_index, new_track)
                            self.track_widgets.insert(self.insert_index, new_track)
                    break
        elif isinstance(src, TrackWidget):  # 拖拽的是整个轨道
            if self.merge_target is not None and src != self.merge_target:
                for name, color, ydata in src.series_info_list[:]:
                    self.merge_target.add_curve(name, color, ydata)
                    src.remove_curve(name)
                self.vlayout.removeWidget(src)
                self.track_widgets.remove(src)
            elif self.insert_index is not None:
                if src in self.track_widgets:
                    idx_from = self.vlayout.indexOf(src)
                    idx_to = self.insert_index
                    if idx_from < idx_to:
                        idx_to -= 1
                    self.vlayout.removeWidget(src)
                    self.vlayout.insertWidget(idx_to, src)
                    self.track_widgets.remove(src)
                    self.track_widgets.insert(idx_to, src)
        a0.accept()

    # 显示插入指示条
    def _show_insert_indicator(self, index):
        self.merge_indicator.hide()
        self.drag_indicator.hide()
        if self.vlayout.indexOf(self.drag_indicator) != -1:
            self.vlayout.removeWidget(self.drag_indicator)
        if index is not None:
            self.vlayout.insertWidget(index, self.drag_indicator)
            self.drag_indicator.show()

    # 显示合并指示条
    def _show_merge_indicator(self, track):
        self.drag_indicator.hide()
        self.merge_indicator.hide()
        if self.merge_indicator.parent() is not None:
            self.merge_indicator.setParent(None)
        self.merge_indicator.setFixedHeight(track.height())
        self.vlayout.insertWidget(self.vlayout.indexOf(track), self.merge_indicator)
        self.merge_indicator.show()

    # 主窗口
class MainWindow1(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("多轨道折线图重叠/独立拖拽示例（优化版）")
        self.setGeometry(100, 100, 1000, 600)
        tracks_data = []
        for name, (vmin, vmax, color) in element_to_simulate.items():
            ydata = [row[name] for row in data]
            tracks_data.append((name, ydata, color))
        self.widget = MultiTrackWidget(tracks_data, [row['深度'] for row in data])
        self.setCentralWidget(self.widget)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    font = QFont("微软雅黑", 10)
    app.setFont(font)
    main_window = MainWindow1()
    main_window.show()
    sys.exit(app.exec_())  # 启动主事件循环